﻿using System;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Documents;

using VSTalk.Engine.Annotations;
using VSTalk.Engine.Core.Context;
using VSTalk.Engine.Core.EntitySession;
using VSTalk.Engine.Core.EnvironmentManagers.UI;
using VSTalk.Engine.Core.Feed;
using VSTalk.Engine.Core.XmppCommunication;
using VSTalk.Engine.Core.XmppCommunication.Services.MessageService;
using VSTalk.Engine.Core.XmppUtils;
using VSTalk.Engine.Utils;
using VSTalk.Engine.ViewModel.Messenger.History;
using VSTalk.Engine.ViewModel.Messenger.MessageBuilder;
using VSTalk.Engine.ViewModel.Messenger.ThreadRenderer;
using VSTalk.Engine.ViewModel.Messenger.ThreadRenderer.Command;
using VSTalk.Model;

using VSTalk.Tools;

namespace VSTalk.Engine.ViewModel.Messenger
{
    public class RoomChatViewModel : IChatViewModel, INotifyPropertyChanged
    {
        private readonly IFrameViewModel _frame;

        private readonly Room _room;

        private RichTextBox _chatTextBox;

        private RichTextBox _messageTextBox;

        private readonly ConversationThread _conversationThread;
        
        private ISubscription _themeUnit;
        private ISubscription _frameUnit;

        private HistoryLoaderViewModel _historyLoader;

        public object Model
        {
            get { return Room; }
        }

        private IModelContext ModelContext
        {
            get { return AppServices.Get<IModelContext>(); }
        }

        private IMessageFeed MessageFeed
        {
            get { return AppServices.Get<IMessageFeed>(); }
        }

        private static ISessionModel SessionModel
        {
            get { return AppServices.Get<ISessionModel>(); }
        }

        private static IWindowManager WindowManager
        {
            get { return AppServices.Get<IWindowManager>(); }
        }

        private static IThemeManager ThemeManager
        {
            get { return WindowManager.ThemeManager; }
        }

        public Room Room
        {
            get { return _room; }
        }

        [UsedImplicitly]
        public IRoomContext Context
        {
            get { return SessionModel.GetRoomContext(Room); }
        }

        public RichTextBox ChatTextBox
        {
            get { return _chatTextBox; }
            set
            {
                _chatTextBox = value;
                PropertyChanged.Notify(() => ChatTextBox);
            }
        }

        public RichTextBox MessageTextBox
        {
            get { return _messageTextBox; }
            set
            {
                _messageTextBox = value;
                PropertyChanged.Notify(() => MessageTextBox);
            }
        }

        public RelayCommand PasteCallStack
        {
            get { return _frame.PasteCallStack; }
        }

        public RelayCommand PasteDebugOutput
        {
            get { return _frame.PasteDebugOutput; }
        }

        public RoomChatViewModel(IFrameViewModel frame, Room room)
        {
            _frame = frame;
            _room = room;

            var chatDocument = new FlowDocument();
            ChatTextBox = new RichTextBox(chatDocument);
            ChatTextBox.IsDocumentEnabled = true;
            ChatTextBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            ChatTextBox.Background = System.Windows.Media.Brushes.Transparent;
            ChatTextBox.IsReadOnly = true;
            _conversationThread = CreateThread(chatDocument);

            MessageTextBox = new RichTextBox(new FlowDocument());
            MessageTextBox.IsDocumentEnabled = true;
            MessageTextBox.Background = System.Windows.Media.Brushes.Transparent;
            MessageTextBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;

            ParseCurrentHistory();
            SetTheme(ThemeManager.ActiveTheme);

            _historyLoader = CreateHistoryLoader();
        }

        private HistoryLoaderViewModel CreateHistoryLoader()
        {
            var client = ModelContext.GetClientByRoom(_room);
            var loader = new HistoryLoaderViewModel(client.Id, XmppIdConverter.Jid(_room), _conversationThread);
            loader.Init();
            return loader;
        }

        public void StartListen()
        {
            _themeUnit = ThemeManager.SubscribeToChange(() => ThemeManager.ActiveTheme, ThemeChanged);
            _frameUnit = _frame.SubscribeToChange(() => _frame.CurrentChat, sender =>
            {
                if (_frame.CurrentChat == this)
                {
                    ChatTextBox.ScrollToEnd();
                }
            });
        }

        private void ThemeChanged(IThemeManager sender)
        {
            SetTheme(ThemeManager.ActiveTheme);
        }

        private void SetTheme(ThemeMode activeTheme)
        {
            MessageTextBox.CaretBrush = ChatTextBox.Document.Foreground = MessageTextBox.Document.Foreground =
                activeTheme == ThemeMode.Dark
                ? System.Windows.Media.Brushes.White
                : System.Windows.Media.Brushes.Black;
        }

        public void StopListen()
        {
            _themeUnit.Unsubscribe();
            _frameUnit.Unsubscribe();
        }

        private ConversationThread CreateThread(FlowDocument document)
        {
            var thread = new ConversationThread(document);
            return thread;
        }

        protected void ParseCurrentHistory()
        {
            ModifyTextBox(() =>
            {
                foreach (var msg in MessageFeed.GetMessages(Predicate))
                {
                    var command = new RenderXmppMessage(_conversationThread.Model, msg);
                    command.Execute();
                }
            });
        }

        protected void ModifyTextBox(Action action)
        {
            ChatTextBox.BeginChange();
            action();
            ChatTextBox.EndChange();
            ChatTextBox.ScrollToEnd();
        }

        public void SendMessage()
        {
            var messageBuilder = MessageBuilderFactory.Create(Room);
            var document = MessageTextBox.Document;
            var xmppMessage = messageBuilder.Create(document);

            var messageService = CommunicationServiceFactory.Create<MessageService>(Room);
            messageService.SendMessage(Room, xmppMessage);
            ClearMessage();
        }

        public bool CanSendMessage()
        {
            var client = ModelContext.GetClientByRoom(Room);
            var context = AppServices.Get<ISessionModel>().GetClientContext(client);
            return context.State == ClientState.Connected &&
                   !IsMessageEmpty();

        }

        public void AppendToMessage(string str)
        {
            MessageTextBox.Document.Blocks.Add(new Paragraph(new Run(str)));
        }

        private void ClearMessage()
        {
            MessageTextBox.Document.Blocks.Clear();
        }

        private bool IsMessageEmpty()
        {
            return new TextRange(MessageTextBox.Document.ContentStart, MessageTextBox.Document.ContentEnd).IsEmpty;
        }

        public void Consume(IMessage message)
        {
            ModifyTextBox(() =>
            {
                var command = new RenderXmppMessage(_conversationThread.Model, message);
                command.Execute();
            });
        }

        public bool Predicate(IMessage message)
        {
            return AddressPatterns.From(_room)(message) ||
                   AddressPatterns.To(_room)(message);

        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}